Module-level declarations

Types

Link copied to clipboard

An auth handler defines who can and should call a specific operation. A new auth handler should be defined for each operation in the dapp.

Link copied to clipboard

This struct represents default auth descriptor configurations that can be used by the client to create credentials that can access the account. These configurations can be defined by the dapp developer by extending the login_config function.

When a user tries to log into a dapp, a login auth descriptor will be created using one of these configurations, and the signer will be stored in such a way that allows the client to sign all transactions it's allowed to without asking for user confirmation.

Using this feature allows the client to provide a better user experience, since the client will be allowed to call what the dapp considers non-sensitive operations.

Proper configuration is required to avoid allowing sensitive operations to be called by the login keypairs that are accessible to the client.

Link copied to clipboard
struct auth_data
Link copied to clipboard

The arguments of the evm_auth operation

Link copied to clipboard

configuration used by the auth module, as defined in the chromia.yml file

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Similar to accounts.rule_variable, but it adds two more variables that only make sense in the context of login auth descriptors:

  • relative_block_time is the difference in timestamp between the auth descriptor creation and its expiration

  • relative_block_height is the difference in block number between the auth descriptor creation and its expiration

These two variables are not useful for normal auth descriptors, as those are created at the same time of definition. In the case of login auth descriptors, the definition happens during the dapp development, while they're created anytime a user logs in. This allows the developer to define rules like a time to live for the login sessions.

Link copied to clipboard
struct signature

An EVM signature

Properties

Link copied to clipboard
val ACCOUNT_ID_PLACEHOLDER: text = "{account_id}"

tag used in auth message construction which will be replaced with the authorizing account ID

Link copied to clipboard
val APP_SCOPE: text = "app"

The constant that defines app level authentication scope. If scope is omitted when defining an auth handler, then it has 'app' scope, i.e. it is used to authenticate operations that do not have their own auth handlers if there is no mount point auth handler for the operation.

Link copied to clipboard
val AUTH_DESCRIPTOR_ID_PLACEHOLDER: text = "{auth_descriptor_id}"

tag used in auth message construction which will be replaced with the authenticating auth descriptor ID

Link copied to clipboard
val BLOCKCHAIN_RID_PLACEHOLDER: text = "{blockchain_rid}"

tag used in auth message construction which will be replaced with blockchain RID

Link copied to clipboard

The name of the default login configuration. This will be used when none is specified

Link copied to clipboard

size of a byte_array representing an EVM address, in bytes

Link copied to clipboard
val EVM_AUTH_OP: text = "ft4.evm_auth"

name of the operation used to authorize through EVM signatures

Link copied to clipboard
val FT_AUTH_OP: text = "ft4.ft_auth"

name of the operation used to authorize through FT signatures

Link copied to clipboard

size of a byte_array representing an FT pubkey, in bytes

Link copied to clipboard
val NONCE_PLACEHOLDER: text = "{nonce}"

tag used in auth message construction which will be replaced with the unique nonce

Link copied to clipboard
val OVERRIDE_PREFIX: text = "__override__"

This prefix could appear before an auth handler's scope to mark that it can be overridden. If two auth handlers are found with the same scope, but one has the override prefix, the other one is used.

Functions

Link copied to clipboard
function _evm_message_hash(message: text): byte_array

Given a message, a valid EVM signature will add some information to it prior to hashing it and cryptographically signing. This function returns the hash of the message as per the EVM signature specifications in EIP-191.

Link copied to clipboard
function _recover_evm_address(message: text, signature: signature): byte_array

Given the message and signature, extracts the evm address that signed it.

Throws "INPUT ERROR" if message is empty.

Link copied to clipboard
function _validate_evm_address(message: text, signature: signature?, account_auth_descriptor: account_auth_descriptor): byte_array

Verifies that the signer of a certain message is the signer of the single-sig auth descriptor used to authorize this operation.

Throws "INPUT ERROR" if the signature is null

Throws "INVALID SIGNATURE" when the signature verification fails. This might happen if:

  • the message signer is not the one associated with the auth descriptor

  • the message that was signed does not correspond to the one passed in the message parameter

Throws if the message is empty

Link copied to clipboard
function _validate_evm_arguments(auth_args: list<gtv>, required_flags: set<text>, account: account, auth_descriptor: account_auth_descriptor): evm_auth_args

Verifies that the arguments passed to the evm_auth operation are valid.

Throws "INPUT ERROR" if the param passed in auth_args has any number of elements other than three.

Throws "UNAUTHORIZED ACCESS" if the auth descriptor does not have the necessary flags to authorize this operation.

Link copied to clipboard
function _validate_evm_signature(op: gtx_operation, auth_op: gtx_operation, flags: list<text>, account: account, auth_descriptor: account_auth_descriptor)

Verifies that the signature found in the evm_auth operation is valid to authorize the operation being called.

Throws if the signature cannot be validated. The most common reasons include:

  • the auth handler for op is not found

  • the signer is not registered in the auth descriptor

  • too few signers signed the operation, in case of a multisig auth descriptor

  • the signer signed the wrong message

  • the auth descriptor does not give sufficient permissions for the operation that is being called

Link copied to clipboard
function _validate_ft4_signature(flags: list<text>, account: account, auth_descriptor: account_auth_descriptor)

Verifies that the signature found in the ft_auth operation is valid to authorize the operation being called.

Can only be called from an operation.

Throws if the signature cannot be validated. Common reasons include:

  • the signers of the auth descriptor did not sign this transaction

  • too few signers signed the operation, in case of a multisig auth descriptor

  • the auth descriptor does not give sufficient permissions for the operation that is being called

Link copied to clipboard
function _validate_multiple_evm_addresses(message: text, signatures: list<signature?>, account_auth_descriptor: account_auth_descriptor): list<byte_array>

Verifies that the message has been authorized by the multi-sig auth descriptor. This implies that the signatures list contains enough signatures to reach the minimum amount of signatures required, and that all signatures are from valid signers from the auth descriptors

Throws "MISMATCHED SIGNER" if a signature is found to be out of place. They must be in the same order they were registered in when the auth descriptor was first created.

Throws "INSUFFICIENT SIGNERS" if the number of valid signatures was below the signature required threshold.

Throws if a signer signed the wrong message, or if the message is empty.

Link copied to clipboard
function add_auth_handler(scope: text, flags: list<text>, message: (gtv) -> text?, resolver: (gtv, byte_array, list<byte_array>) -> byte_array??): map<text, _auth_handler>

Utility function that allows easily adding an auth handler. It should be used by dapps, while library developers should generally prefer using add_overridable_auth_handler instead, allowing dapps to override the default handler.

Throws if scope is not valid

Example:

operation my_op() { ... }

@extend(auth.auth_handler)
function () = auth.add_auth_handler(
rell.meta(my_op).mount_name,
flags,
message_function,
resolver_function
);
Link copied to clipboard
function add_login_config(name: text, flags: list<text>, rules: gtv?): map<text, _login_config>

Utility function to help extend the login_config function.

Example usage:

@extend(auth.login_config)
function () = add_login_config(name, flags, rules);
Link copied to clipboard
function add_overridable_auth_handler(scope: text, flags: list<text>, message: (gtv) -> text?, resolver: (gtv, byte_array, list<byte_array>) -> byte_array??): map<text, _auth_handler>

Like add_auth_handler, but the auth handler here defined will not be used if a different auth handler for the same scope was created with add_auth_handler.

As normal, however, for an operation with mount point a.b.operation, an overridable auth handler with scope a.b will take precedence over a non-overridable auth handler with scope a

Throws if scope is not valid

Link copied to clipboard
@extendable function after_authenticate(account: account, account_auth_descriptor: account_auth_descriptor?)

This function can be extended by users to add custom logic after the authentication of an operation.

Throws when any extension of this function throws.

Link copied to clipboard
function args(): gtv

Retrieves the arguments of the operation that led to this function call

Can only be called from an operation.

Link copied to clipboard
@extendable function auth_handler(): map<text, _auth_handler>

Extendable function used to define auth handlers. When called it returns a map of all auth handlers defined in the dapp.

The map pairs up the auth handlers with their scopes.

Throws if any extension of this function throws.

Link copied to clipboard
function authenticate(): account

calls authenticate_and_return_context and returns just the account

Link copied to clipboard

Verifies that:

  1. the transaction is properly signed

  2. the signer(s) is allowed to call that operation in the name of an account

  3. the account is not currently rate limited

For point 2, auth flags of the auth descriptor that signed the operation are checked.

This function expects to find an auth operation before the one we're currently in. That operation receives as parameters the account the user wants to access, and the auth descriptor that allows them to access it.

Given that information, this function checks that the requirements for the current operation (found in the _auth_handler) are satisfied.

Can only be called from an operation.

The code of this function can be expanded by the developers by extending either of before_authenticate and after_authenticate.

Throws "MISSING AUTH OP" if there is no previous operation in the transaction, or it is not an auth operation.

Throws "INVALID AUTH DESCRIPTOR" if the authenticating auth descriptor is not accepted by the _auth_handler's resolver.

Throws "EXPIRED AUTH DESCRIPTOR" if the auth descriptor used to authenticate has expired.

Throws when the user is for some reason not allowed to call this operation. The most common reasons include:

  • the account the user wants to access does not exist

  • the auth descriptor that the user uses to authenticate does not exist, or it is not attached to the account that is being accessed

  • the account does not have enough rate limit points

  • the rules for the auth descriptor cannot be parsed

  • the requirements for the operation cannot be found

  • the auth descriptor does not give the user sufficient permissions given the operation's requirements

  • any additional requirement added by extending the before_authenticate and after_authenticate functions is infringed.

  • any part of the _auth_handler is broken (the message is improperly formatted, or either of the two functions found in the message_formatter or resolver fields throw)

Link copied to clipboard
@extendable function before_authenticate(account: account, account_auth_descriptor: account_auth_descriptor)

This function can be extended by users to add custom logic before the authentication of an operation.

Throws when any extension of this function throws.

Link copied to clipboard
Link copied to clipboard
function block_time(integer: integer): rule_parameters
Link copied to clipboard
function create_message_from_template(evm_auth_args: evm_auth_args, message_template: text, args: list<gtv>, nonce: text): text

Creates the message that should be signed on an EVM wallet to approve the given operation.

Throws "MISCONFIGURED MESSAGE" if the template doesn't have a nonce placeholder or a blockchain RID placeholder, both of which should be added to the template in advance by utils.make_auth_message

Link copied to clipboard
function equals(rule_parameters: rule_parameters): rule_expression
Link copied to clipboard

Extracts the account ID and auth descriptor ID from the args of the auth operation, which are the operations supported by is_auth_op

Link copied to clipboard

Extracts the account ID passed to the auth operation, which is any operation supported by is_auth_op.

Throws "INVALID AUTH OP" if the operation passed is not an auth operation

Throws "INVALID AUTH ARGS" if the auth operation passed has no arguments

Link copied to clipboard

Extracts the account and account_auth_descriptor from the args of the auth operation, which is one of the operations supported by is_auth_op.

Throws if the account referred to in auth_args does not exist

Throws "MISSING AUTH DESCRIPTOR" if the auth descriptor referred to in auth_args is not associated with the account id specified in auth args or does not exist altogether.

Link copied to clipboard

Creates message from operation name and arguments. It is used when evm_auth is used to authenticate a user, but auth message is not specified in the auth handler of the operation.

The message will contain the operation name and all parameters, formatted in a way that favors readability of every parameter. It is not expected to be a sensible message for end users to read, as the parameters will not have a label identifying what they represent.

Link copied to clipboard
function get_auth_details_from_auth_operation(): (account_id: byte_array, auth_descriptor_id: byte_array)?

Retrieves the account and auth descriptor ID from the auth operation that precedes the current operation, if any. Returns null if no operation precedes the current one, or it is not an auth operation.

Can only be called from an operation.

Link copied to clipboard
function get_auth_flags(op_name: text): list<text>

Returns the flags needed to call a certain operation.

Throws if the auth handler for op_name cannot be found.

Link copied to clipboard
function get_auth_handler(op_name: text): _auth_handler

Finds the auth handler for the provided operation name among all the auth handlers defined in this dapp as extensions of auth_handler.

All auth handlers will be in a map<scope: text, _auth_handler>

A scope can be:

  • an operation name, in the way it is called from the client: a.b.my_op

  • an operation name prefixed with OVERRIDE_PREFIX: OVERRIDE_PREFIX + "a.b.my_op"

  • a mount point: a.b

  • the value of APP_SCOPE

When calling an operation, the auth handler that will be used is (in this order):

  • the auth handler with the operation name as scope, if it exists

  • the auth handler with the operation name and OVERRIDE_PREFIX as scope, if it exists

  • the auth handler with the closest mount point as scope, if it exists

  • the auth handler with the app scope, if it exists

For example, an operation mounted on a.b.my_op will look for, in this order:

  • the auth handler with scope a.b.my_op

  • the auth handler with scope OVERRIDE_PREFIX + "a.b.my_op"

  • the auth handler with scope a.b

  • the auth handler with scope a

  • the auth handler with the app scope

Throws if op_name is not valid

Throws "MISSING HANDLER" if no auth handler is defined for this operation, nor any mount point above it.

Link copied to clipboard
function get_auth_message_template(op_name: text, op_args: gtv?): text

Returns the auth message template that is needed to authorize a certain operation, given its arguments. It will either use the one defined in auth_handler.message_formatter, or create a default one from the args if the former does not exist.

Throws if the auth handler for op_name cannot be found.

Link copied to clipboard
function get_evm_signatures(): (signers: list<byte_array>, signatures: list<signature>)

Retrieves the signers and signatures from the evm_signatures operation. Returns empty lists if the operation is not found.

Can only be called from an operation.

Throws "MISMATCHED SIGNATURES" if the length of the two lists is mismatched in the evm_signatures operation.

Throws "NULL EVM SIGNATURE" if any signature is null.

Link copied to clipboard
function get_first_allowed_auth_descriptor(op_name: text, args: gtv, account_id: byte_array, ad_ids: list<byte_array>): byte_array?

Calculates the best auth descriptor to use for the given account when calling the given operation with the given arguments. It assumes that the auth descriptors have all the required auth flags.

If the operation has an auth handler with a resolver function, it will be used to decide which auth descriptor to return. Otherwise, the first auth descriptor on the list will be returned.

If no auth descriptor is returned but ad_ids is not empty, the auth handler resolver function is to blame.

Throws if the auth handler for this operation is not found or its resolver is a function that throws.

function get_first_allowed_auth_descriptor_by_signers(op_name: text, args: gtv, account_id: byte_array, signers: list<byte_array>): byte_array?

Like get_first_allowed_auth_descriptor, but instead of accepting a list of auth descriptors it takes a list of accounts.auth_descriptor_signer IDs. The list of auth descriptors to check will be the list of all the auth descriptors these signers participate in, provided that they can be used for authenticating this operation (i.e., they have the necessary flags to allow them).

Throws if the auth handler for this operation is not found or its resolver is a function that throws.

Link copied to clipboard
function get_mount_scope_auth_handler(auth_handlers: map<text, _auth_handler>, op_name: text): _auth_handler?

Retrieves the auth handler that should be applied to the given operation name, only looking through mount scope auth handlers.

Example: If the operation a.b.my_op is passed, it will return the first auth handler found from this list:

  • an auth handler with scope a.b

  • an auth handler with scope a

  • null regardless of whether an auth handler with scope a.b.my_op exists.

Link copied to clipboard
Link copied to clipboard
function greater_than(rule_parameters: rule_parameters): rule_expression
Link copied to clipboard

Checks whether auth operations cannot be used with provided operation.

Auth operations don't perform any checks, which means that if they're used with an operation that does not call authenticate, users could pass a lot of signatures to it without being rate limited.

For example, they could send evm_auth followed by a nop, and no logic would be performed on chain. However, the transaction would be stored, allowing a malicious user to store a lot of data on chain very quickly (since rate limiting doesn't take place).

The next operation thus be one that either rate limits or checks the auth operation in some way.

Throws "AUTH OP PARSING ERROR" if the chromia.yml cannot be parsed for this parameter, e.g. if some elements are not text or there are duplicates.

Link copied to clipboard

Verifies whether the operation passed is an auth operation, that is one of:

  • EVM_AUTH_OP

  • FT_AUTH_OP

Link copied to clipboard

Checks whether evm_signatures operation can be used with provided operation.

We don't keep track of nonce for keys used with evm_signatures, therefore if not used with care, authorizing an operation with evm_signatures could lead to replay attack.

In order to use it with an operation, the operation has to be whitelisted. By default, library only allows evm_signatures to be used with certain operations.

evm_signatures is safe to use in combination with ft_auth or evm_auth operation.

However, if used as only way to authorize an operation, the operation has to ensure that it cannot be called more than once with same parameters.

One way to avoid that is if the operation creates an entity that has some key value that is derived from operation parameters.

Throws "EVM SIG PARSING ERROR" if the chromia.yml cannot be parsed for this parameter, e.g. if some elements are not text or there are duplicates.

Link copied to clipboard

Verifies whether the operation passed is evm_signatures

Link copied to clipboard
function join_text_list(components: list<text>, count: integer): text

Utility function that builds a mount scope given all the parts of it and a number indicating the depth. Example: join_text_list(["a", "b", "c"], 2) outputs "a.b"

Throws "INPUT ERROR" if:

  • count is 0, or

  • count is greater or equal to the components size

Link copied to clipboard
function less_or_equal(rule_parameters: rule_parameters): rule_expression
Link copied to clipboard
function less_than(rule_parameters: rule_parameters): rule_expression
Link copied to clipboard
@extendable function login_config(): map<text, _login_config>

This function can be extended by users to add custom login configurations.

Throws if any extension of this function throws.

Link copied to clipboard
function login_rules(rules_list: list<rule_expression>): gtv

Converts a list of rule_expression to a complex rule that can be used inside an auth descriptor's rules field

Throws "EMPTY RULES" if the rules_list is empty

Link copied to clipboard

Alias for map_rule

Link copied to clipboard
function map_rule(rule: rule_expression): gtv

Converts a rule_expression to a gtv config that can be used inside an auth descriptor's rules field.

Link copied to clipboard
function op_count(integer: integer): rule_parameters
Link copied to clipboard

Same as accounts.block_height, but using the relative rule variable.

Link copied to clipboard

Same as accounts.block_time, but using the relative rule variable.

Link copied to clipboard

Ensures that this operation is authorizing an operation which is whitelisted for usage with evm_signatures

The operation that is being authorized should be the next operation in the transaction, but it's allowed to have an auth operation or a strategy operation between this operation and the authorized one.

Throws "EVM SIG FORBIDDEN" if:

  • the operation that is being authorized is not whitelisted,

  • there's no operation after this one, or

  • there is only one operation after this one, and it's an auth or strategy operation

Can only be called from an operation, and it should only be called by evm_signatures

Link copied to clipboard

Verifies that the next operation is one that will make use of the auth operation that is being called.

Can only be called from an operation, and it should only be called by auth operations.

Throws "AUTH OP FORBIDDEN" if the next operation is blacklisted or this is the last operation in the transaction

Link copied to clipboard

Verifies that the scope name is valid, i.e. that it matches this regex: /^\w+(\.\w+)*$/ any single word (matched by \w+) or sequence of such words separated by dots is valid. name may not start or end with a dot.

Throws "INVALID SCOPE" if the name does not match

Link copied to clipboard
function try_fetch_auth_descriptor(account: account, auth_descriptor_id: byte_array): account_auth_descriptor?

Retrieves an account_auth_descriptor given the account it's associated with and the ID of the auth descriptor itself. Returns null if not found.

Link copied to clipboard
function ttl(millis: integer): gtv

Creates a time-to-live rule, which makes the auth descriptor expire after a certain amount of time since its creation.

Link copied to clipboard
function valid_scope_name(scope: text): text

Ensures that the scope is valid, also trimming leading and trailing whitespace.

Throws if scope is not valid

Link copied to clipboard
function verify_signers(ft_and_evm_signers: list<byte_array>)

Verifies whether provided list of signers have signed the operation/transaction. FT (GTX) signers are checked against GTX signers and signatures fields, while EVM signers are checked against signers and signatures provided in external.auth.evm_signatures operation. Only used when the signers are not part of an auth descriptor, for example when an auth descriptor is being registered.

Can only be called from an operation.

Throws "MISSING AUTH OP" when no auth operation is found to be preceding the current one but the message template requires the account and/or auth descriptor ID that would be found there.

Throws if the auth handler for the current operation cannot be found.

Throws if the signers cannot be verified. Common cases are:

  • the EVM signers and signatures in evm_signers are in different order

  • some EVM signers have not signed the message, or any of them have signed a wrong message

  • some FT signers did not sign the transaction

  • the message for the EVM signers was empty

Link copied to clipboard
function verify_signers_with_message(ft_and_evm_signers: list<byte_array>, message: text)

Does what verify_signers does, but receives the message as a parameter instead of using the auth message of the current operation.

Can only be called from an operation.

Throws "MISMATCHED SIGNATURES" if any of the signers and signatures on the evm_signatures operation don't match in the exact order they're found.

Throws "MISSING SIGNATURE" if:

  • an EVM signer is specified but the signature is not found in the evm_signatures operation.

  • an FT signer is specified but the signature is not found in the signers of the transaction.

Throws "UNSUPPORTED SIGNER" if a signer passed is neither an EVM nor an FT signer, which means the byte array length is different from EVM_ADDRESS_SIZE and FT_PUBKEY_SIZE

Throws if the message is empty.

Throws if any signatures in evm_signatures is null or missing, or if there's extra signatures in that operation.